<?
    /***********************************************************
     * CGL/CGLObject : This is the base class which all CGL 
     * controls inherit. 
     * ---------------------------------------------------------
     * (c) 2007, 2008 Joe Chrzanowski
     * ---------------------------------------------------------
     * This class controls the management of the element's 
     * starting and ending HTML tags as well as all attributes
     *
     * Essentially a class for creating custom HTML controls
     ***********************************************************/
     
    // dummy objects to hold the attributes
    class Attributes {
        var $_vals = array();
        
        function __construct() {}
        function __get($var) {
            return $this->_vals[$var];
        }
        function Count() { return count($this->_vals); }
        // for Attributes->id = "val"
        function __set($var,$val) {
            $this->Set($var,$val);
        }
        // for Attributes->Set($varname, "value");
        function Set($var,$val) {
            $this->_vals[$var] = $val;
        }
    }
    class StyleAttributes extends Attributes {
        function __toString() {
            $parts = array();
            foreach ($this->_vals as $var => $val) {
                $parts[] = "$var:$val";
            }
            return implode(";",$parts);
        }
    }
    class HTMLAttributes extends Attributes {
        function __toString() {
            $buf = array();
            foreach ($this->_vals as $attrib => $value) {
                if (strlen(trim($value)) == 0) continue;
                
                $buf[] = "$attrib='$value'";
            }
            return implode(" ",$buf);
        }
    }
     
    // this class is the core of cgl
    // this class is what handles the object heirarchies and manages actual data
    class HTMLControl {
        private $_name = null;              // unique id for this control (yes user modifiable because it'll appear in the html)
        var $styles = null;                 // All 'style' properties
        var $attributes = null;             // All HTML Attributes
        
        private $_tag = null;               // this is set when RenderTag is called.  Saves the tag for running RenderClosingTag()
        private $_children = array();
        private $_innerhtml = '';
        private $_endtag = true;
        
        private $_no_end_tag = array(
            "input","br","hr","img"
        );
        
        function __construct($tag, $innerhtml = '',$attributes = null, $styles = null) {
            $this->_tag = $tag;
            
            if (is_object($innerhtml))
                $this->AddChild($innerhtml);
            else
                $this->_innerhtml = $innerhtml;
        
            $this->_name = ($this->_name == null ? md5(microtime()) : $this->_name); // generate the unique id
            
            $this->styles = new StyleAttributes();
            $this->attributes = new HTMLAttributes();
            
            $this->attributes->id = $this->_name;  // set the html attribute 'id' with the new id
            
            /* Attach default styles and attributes - if applicable */
            if (is_array($attributes)) {
                foreach ($attributes as $attrib => $value) {
                    $this->attributes->Set($attrib,$value);
                }
            }
            
            if (is_array($styles)) {
                foreach ($styles as $attrib => $value) {
                    $this->styles->Set($attrib,$value);
                }
            }
            
            // certain tags should be rendered like <br /> <hr /> instead of <br></br> and <hr></hr>
            // most tags should be closed the normal way - the only reason there may be exceptions
            // is if there is no content for that tag 100% of the time.
            //
            // NOTE: that these tags are NOT allowed to have children
            if (array_search(strtolower($tag),$this->_no_end_tag) !== false) $this->_endtag = false;
        }
        
        function SetHTML($html) {
            $this->_innerhtml = $html;
        }
        function AddHTML($html) {
            $this->_innerhtml .= $html;
        }
        
        //////////////////////////////////////////////////////////
        // Functions for handling children
        function FindChild($name) {
            if ($this->_children[$name]) return $this->_children[$name];
            else return null;
        }
        function AddChild($element) { $this->_children[] = $element; }
        function GetChildren() { return $this->_children; }
        function CountChildren() { return count($this->_children); }
        function HasChildren() { return count($this->_children) > 0; }
        function HasChild($name) {
            if ($this->FindChild($name) != null) return true;
            else return false;
        }
        
        function DebugChildren($indent = 0) {
            foreach ($this->_children as $child) {
                echo str_repeat("&nbsp;",$indent*4);
                if (is_object($child)) {
                    echo get_class($child) . ($child->HasChildren() ? ": listing children" : "") . "<br>";
                    $child->DebugChildren($indent+1);
                }
                else echo "$child is a non-object<br>";
            }
        }
        
        function RenderChildren() {
            $buf = "";
            foreach ($this->_children as $child) {
                if (!is_object($child)) echo "COULDN'T RENDER CHILD: " . $child . "<br>";
                else $buf .= $child->Render(true);
            }
            return $buf;
        }
        function Type() { return get_class($this); }
        
        //////////////////////////////////////////////////////////
        // Rendering Functions
        // the $return argument is overloaded for the __toString
        // method and for recursive renderings
        function Render($return = false) {
            $buf = "";
            $buf .= $this->RenderTag();
            if ($this->_endtag) {
                $buf .= $this->RenderChildren();
                $buf .= $this->_innerhtml;
                $buf .= $this->RenderClosingTag();
            }
            
            if ($return) return $buf;
            else echo $buf;
        }
        function __toString() {
            return $this->Render(true);
        }
        
        //////////////////////////////////////////////////////////
        // For Rendering Tags
        function RenderTag() {
            // (cast into a string)
            if ($this->styles->Count() > 0)
                $this->attributes->style = $this->styles->__toString();
            $tag = $this->_tag;
            
            if (!$this->_endtag)
                return "<$tag " . $this->attributes->__toString() . " />";
            else
                return "<$tag " . $this->attributes->__toString() . ">";
        }
        // Print the closing tag for this element
        function RenderClosingTag() {
            if ($this->_tag) {
                return "</" . $this->_tag . ">";
            }
            else return '';
        }    
        
        ///////////////////////////////////////////////////////
        // Support for old methods of doing things
        // basically just redirects the old functions to their
        // new counterparts.  Do not use these if possible.
        function SetAttribute($var,$val) { $this->attributes->Set($var,$val); }
        function GetAttribute($var) { return $this->attributes->$var; }
        function SetStyle($var,$val) { $this->styles->Set($var,$val); }
        function AddStyle($var,$val) { $this->styles->Set($var,$val); }
        function GetStyle($part) { return $this->styles->$var; }
    }
    
    // legacy
    class CGLObject extends HTMLControl {} 
    class HTMLElement extends HTMLControl {}
    
    
    
    
    
    
    
    
    // and a few random shortcuts that get used a lot:
    class PrevButton extends HTMLControl {
        function __construct($value = "Prev") {
            parent::__construct("input",null,array("class"=>"prev","type"=>"button","value"=>$value));
        }
    }
    class NextButton extends HTMLControl {
        function __construct($value = "Next") {
            parent::__construct("input",null,array("class"=>"next","type"=>"button","value"=>$value));
        }
    }
?>